#include <stdio.h>
#include <stdarg.h>
#include "rgb_lcd.h"
#include "color.h"
#include "tim.h"

uint16_t *base_addr = (uint16_t *)((uint32_t)0xC0000000);

void rgb_colors_fill(uint16_t start_x, uint16_t start_y, uint16_t end_x, uint16_t end_y, uint16_t *color)
{
    uint16_t width = end_x - start_x + 1;
    uint16_t height = end_y - start_y + 1;
    uint32_t timeout = 0; 

    DMA2D->OOR = 800 - width;
    DMA2D->FGMAR = (uint32_t)color;  // 源地址
    DMA2D->OMAR = (uint32_t)&base_addr[start_y * 800 + start_x];
    DMA2D->NLR = (uint32_t)(width << 16) | height;
    DMA2D->CR |= 1 << 0;
	DMA2D->CR |= DMA2D_IT_TC|DMA2D_IT_TE|DMA2D_IT_CE;  // 开启中断
}

uint16_t RGB565(uint32_t color888)
{
    uint8_t r = (color888 >> 16) & 0xFF; // 提取红色分量
    uint8_t g = (color888 >> 8) & 0xFF;  // 提取绿色分量
    uint8_t b = color888 & 0xFF;         // 提取蓝色分量

    uint16_t r5 = (r >> 3) & 0x1F; // 红色 5 位
    uint16_t g6 = (g >> 2) & 0x3F; // 绿色 6 位
    uint16_t b5 = (b >> 3) & 0x1F; // 蓝色 5 位

    return (r5 << 11) | (g6 << 5) | b5; // 合并成 RGB565
}

uint16_t touch_x, touch_y;

static void iic_start();
static void iic_stop();
static uint8_t iic_wait_ack();
static void iic_ack();
static void iic_nack(void);
static void iic_send_byte(uint8_t data);
static uint8_t iic_read_byte(uint8_t ack);
static uint8_t wr_reg(uint16_t reg, uint8_t *buf, uint8_t len);
static uint8_t wr_reg(uint16_t reg, uint8_t buf);
static void rd_reg(uint16_t reg, uint8_t *buf, uint8_t len);

void touchInit()
{
    // 初始化引脚
    GPIO_InitTypeDef hgpio;

    // RST I8
    __HAL_RCC_GPIOI_CLK_ENABLE(); 
    hgpio.Pin = GPIO_PIN_8;
    hgpio.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
    hgpio.Pull = GPIO_PULLUP;         /* 上拉 */
    hgpio.Speed = GPIO_SPEED_HIGH;    /* 高速 */
    HAL_GPIO_Init(GPIOI, &hgpio);

    // INT H7
    __HAL_RCC_GPIOH_CLK_ENABLE();
    hgpio.Pin = GPIO_PIN_7;
    hgpio.Mode = GPIO_MODE_INPUT; /* 输入 */
    HAL_GPIO_Init(GPIOH, &hgpio);

    // SCL H6
    hgpio.Pin = GPIO_PIN_6;           
    hgpio.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
    HAL_GPIO_Init(GPIOH, &hgpio);

    // SDA I3
    hgpio.Pin = GPIO_PIN_3;           
    hgpio.Mode = GPIO_MODE_OUTPUT_OD; /* 开漏输出 */
    hgpio.Pull = GPIO_PULLUP;         /* 上拉 */
    HAL_GPIO_Init(GPIOI, &hgpio);

    iic_stop();  /* 初始化电容屏的I2C总线 */
    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_RESET);  /* 复位 */
    HAL_Delay(10);
    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET);  /* 释放复位 */
    HAL_Delay(10);

    /* INT引脚模式设置, 输入模式, 浮空输入 */
    hgpio.Pin = GPIO_PIN_7;
    hgpio.Mode = GPIO_MODE_INPUT;                /* 输入 */
    hgpio.Pull = GPIO_NOPULL;                    /* 不带上下拉，浮空模式 */
    HAL_GPIO_Init(GPIOH, &hgpio);                /* 初始化INT引脚 */

    uint8_t temp = 0X02;
    wr_reg(GT9XXX_CTRL_REG, temp);    /* 软复位GT9XXX */

    HAL_Delay(10);

    temp = 0X00;
    wr_reg(GT9XXX_CTRL_REG, temp);    /* 结束复位, 进入读坐标状态 */
}

static void rd_reg(uint16_t reg, uint8_t *buf, uint8_t len)
{
    uint8_t i;
    iic_start();
    iic_send_byte(GT9XXX_CMD_WR);    /* 发送写命令 */
    iic_wait_ack();
    iic_send_byte(reg >> 8);         /* 发送高8位地址 */
    iic_wait_ack();
    iic_send_byte(reg & 0XFF);       /* 发送低8位地址 */
    iic_wait_ack();
    iic_start();
    iic_send_byte(GT9XXX_CMD_RD);    /* 发送读命令 */
    iic_wait_ack();

    for (i = 0; i < len; i++) {
        buf[i] = iic_read_byte(i == (len - 1) ? 0 : 1);  /* 读取数据 */
    }

    iic_stop();  /* 产生一个停止条件 */
}

static uint8_t wr_reg(uint16_t reg, uint8_t *buf, uint8_t len)
{
    uint8_t i;
    uint8_t ret = 0;
    iic_start();
    iic_send_byte(GT9XXX_CMD_WR);    /* 发送写命令 */
    iic_wait_ack();
    iic_send_byte(reg >> 8);         /* 发送高8位地址 */
    iic_wait_ack();
    iic_send_byte(reg & 0XFF);       /* 发送低8位地址 */
    iic_wait_ack();

    for (i = 0; i < len; i++)
    {
        iic_send_byte(buf[i]);       /* 发数据 */
        ret = iic_wait_ack();

        if (ret)break;
    }

    iic_stop();  /* 产生一个停止条件 */
    return ret;
}

static uint8_t wr_reg(uint16_t reg, uint8_t buf)
{
    uint8_t ret = 0;
    iic_start();
    iic_send_byte(GT9XXX_CMD_WR);    /* 发送写命令 */
    iic_wait_ack();
    iic_send_byte(reg >> 8);         /* 发送高8位地址 */
    iic_wait_ack();
    iic_send_byte(reg & 0XFF);       /* 发送低8位地址 */
    iic_wait_ack();

    iic_send_byte(buf);       /* 发数据 */
    ret = iic_wait_ack();

    iic_stop();  /* 产生一个停止条件 */
    return ret;
}

bool scan()
{
    uint8_t buf;
    rd_reg(GT9XXX_GSTID_REG, &buf, 1);  /* 读取触摸点的状态 */

    // 该寄存器在每次读取后，如果 bit7 有效，则必须写 0，清除这个位，否则不会输出下一次数据！
    if (buf & 0X80) {
        wr_reg(GT9XXX_GSTID_REG, 0); /* 清标志 */
    }

    // 如果低 4 位有值，则表示有触摸点被按下
    if (buf & 0XF) {
        uint8_t position[4];
        rd_reg(GT9XXX_TP1_REG, position, 4); /* 读取触摸坐标 */
        touch_x = position[0] | (position[1] << 8);
        touch_y = position[2] | (position[3] << 8);
        return true;
    }

    return false;
}

static void iic_start()
{
    IIC_SDA(1);IIC_SCL(1);delay_us(2);IIC_SDA(0);delay_us(2);IIC_SCL(0);delay_us(2);
}

static void iic_stop()
{
    IIC_SDA(0);IIC_SCL(1);delay_us(2);IIC_SDA(1);delay_us(2);
}

static uint8_t iic_wait_ack()
{
    uint8_t waittime = 0;
    uint8_t rack = 0;

    IIC_SDA(1);delay_us(2);IIC_SCL(1);delay_us(2);

    while (CT_READ_SDA) /* 等待应答 */
    {
        waittime++;

        if (waittime > 250)
        {
            iic_stop();
            rack = 1;
            break;
        }
        delay_us(2);
    }

    IIC_SCL(0);delay_us(2);

    return rack;
}

static void iic_ack()
{
    IIC_SDA(0);delay_us(2);IIC_SCL(1);delay_us(2);IIC_SCL(0);delay_us(2);IIC_SDA(1);delay_us(2);
}

static void iic_nack()
{
    IIC_SDA(1);delay_us(2);IIC_SCL(1);delay_us(2);IIC_SCL(0);delay_us(2);
}

static void iic_send_byte(uint8_t data)
{
    for (uint8_t t = 0; t < 8; t++) {
        IIC_SDA((data & 0x80) >> 7); /* 高位先发送 */
        delay_us(2);IIC_SCL(1);delay_us(2);IIC_SCL(0);
        data <<= 1;     /* 左移1位,用于下一次发送 */
    }

    IIC_SDA(1);      /* 发送完成, 主机释放SDA线 */
}

static uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t receive = 0;

    for (uint8_t i = 0; i < 8; i++ )    /* 接收1个字节数据 */
    {
        receive <<= 1;          /* 高位先输出,所以先收到的数据位要左移 */
        IIC_SCL(1);delay_us(2);

        if (CT_READ_SDA) receive++;

        IIC_SCL(0);delay_us(2);
    }

    if (!ack) iic_nack(); else iic_ack();

    return receive;
}
